home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / pmake / customs / export.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-11-15  |  10.5 KB  |  415 lines

  1. /*-
  2.  * export.c --
  3.  *    Program to handle the interaction with the customs agent over
  4.  *    an exported process. This should, perhaps, be generalized so
  5.  *    other programs can use it, but...maybe a library...
  6.  *
  7.  * Copyright (c) 1988, 1989 by the Regents of the University of California
  8.  * Copyright (c) 1988, 1989 by Adam de Boor
  9.  * Copyright (c) 1989 by Berkeley Softworks
  10.  *
  11.  * Permission to use, copy, modify, and distribute this
  12.  * software and its documentation for any non-commercial purpose
  13.  * and without fee is hereby granted, provided that the above copyright
  14.  * notice appears in all copies.  The University of California,
  15.  * Berkeley Softworks and Adam de Boor make no representations about
  16.  * the suitability of this software for any purpose.  It is provided
  17.  * "as is" without express or implied warranty.
  18.  */
  19. #ifndef lint
  20. static char *csid =
  21. "$Id: export.c,v 1.15 89/11/14 13:46:02 adam Exp $ SPRITE (Berkeley)";
  22. #endif lint
  23.  
  24. #include    <sys/time.h>
  25. #include    <signal.h>
  26. #include    <stdio.h>
  27. #include    <sys/wait.h>
  28. #include    <sys/ioctl.h>
  29. #include    <errno.h>
  30. extern int errno;
  31. #include    <netdb.h>
  32. #include    <sys/file.h>
  33.  
  34. #include    "customs.h"
  35.  
  36. int                  rmt;      /* Socket to remote process */
  37. int                  ret;       /* Socket server will call us back on with
  38.                  * exit status */
  39. u_long                  id;        /* Permit id under which the process is
  40.                  * running */
  41. struct sockaddr_in    server;    /* Address of server running the process */
  42. extern int            customs_Socket;
  43. extern struct timeval    customs_RetryTimeOut;
  44.  
  45. union wait            status;
  46. int            exitSeen = 0;
  47. void                  DoExit();
  48.  
  49. /*-
  50.  *-----------------------------------------------------------------------
  51.  * PassSig --
  52.  *    Catch a signal and pass it along. We only kill ourselves with the
  53.  *    signal when we receive the exit status of the remote process.
  54.  *
  55.  * Results:
  56.  *    None.
  57.  *
  58.  * Side Effects:
  59.  *    An RPC call is made to the import server to deliver the signal.
  60.  *
  61.  *-----------------------------------------------------------------------
  62.  */
  63. PassSig(signo)
  64.     int        signo;    /* The signal number we've received */
  65. {
  66.     Kill_Data      packet;
  67.     Rpc_Stat      status;
  68.     
  69.     packet.id = id;
  70.     packet.signo = signo;
  71.  
  72.     status = Rpc_Call (ret, &server, (Rpc_Proc)CUSTOMS_KILL,
  73.                sizeof(packet), (Rpc_Opaque)&packet,
  74.                0, (Rpc_Opaque)0,
  75.                CUSTOMS_NRETRY, &customs_RetryTimeOut);
  76.     if (status != RPC_SUCCESS) {
  77.     printf("Customs_Kill(%d): %s\n", signo, Rpc_ErrorMessage(status));
  78.     exit(1);
  79.     }
  80. }
  81.  
  82. /*-
  83.  *-----------------------------------------------------------------------
  84.  * Drain --
  85.  *    Wait for the remote socket to become writable again, handling
  86.  *    any output from the remote side in the mean time.
  87.  *
  88.  * Results:
  89.  *    None.
  90.  *
  91.  * Side Effects:
  92.  *    If 'what' is RPC_WRITABLE, Transfer is reinstalled as the stream
  93.  *    server for stdin...
  94.  *
  95.  *-----------------------------------------------------------------------
  96.  */
  97. /*ARGSUSED*/
  98. void
  99. Drain(stream, arg, what)
  100.     int              stream;
  101.     Rpc_Opaque       arg;
  102.     int              what;
  103. {
  104.     extern void      Transfer();
  105.     
  106.     if (what & RPC_READABLE) {
  107.     /*
  108.      * Transfer any data from remote side
  109.      */
  110.     Transfer(rmt, 1);
  111.     }
  112.     if (what & RPC_WRITABLE) {
  113.     /*
  114.      * Socket has drained enough, reinstall the regular handlers
  115.      * for both streams
  116.      */
  117.     Rpc_Watch(rmt, RPC_READABLE, Transfer, (Rpc_Opaque)1);
  118.     Rpc_Watch(0, RPC_READABLE, Transfer, (Rpc_Opaque)rmt);
  119.     }
  120. }
  121.  
  122. /*-
  123.  *-----------------------------------------------------------------------
  124.  * Transfer --
  125.  *    Transfer data from one source to another.
  126.  *
  127.  * Results:
  128.  *    None.
  129.  *
  130.  * Side Effects:
  131.  *    Data are read from source and written to dest.
  132.  *
  133.  *-----------------------------------------------------------------------
  134.  */
  135. void
  136. Transfer (source, dest)
  137.     int        source;
  138.     int        dest;
  139. {
  140.     char    buf[BUFSIZ];
  141.     int        cc;
  142.  
  143.     cc = read(source, buf, sizeof(buf));
  144.     if (cc < 0) {
  145.     perror ("read");
  146.     printf("source = %d, dest = %d\n", source, dest);
  147.     return;
  148.     } else if (cc == 0) {
  149.     if (source == 0) {
  150.         /*
  151.          * When we reach the end-of-file for our input, we want the remote
  152.          * process to reach that state, too, so we half-shutdown our socket
  153.          * to it.
  154.          */
  155.         if (shutdown(rmt, 1) < 0) {
  156.         perror("shutdown");
  157.         exit(3);
  158.         }
  159.     } else if (exitSeen) {
  160.         /*
  161.          * We've gotten an EOF on the socket and customs has already sent
  162.          * us an exit signal, so perform the exit now.
  163.          */
  164.         DoExit();
  165.     }
  166.     Rpc_Ignore(source);
  167.     } else if (write (dest, buf, cc) != cc) {
  168.     if (errno != EWOULDBLOCK) {
  169.         if (errno == EPIPE) {
  170.         if (dest == rmt) {
  171.             /*
  172.              * Connection to remote side was lost. This means that
  173.              * both the process and the server died, so there's
  174.              * no point in waiting for the exit status...
  175.              */
  176.             printf ("*** connection closed.\n");
  177.             if (exitSeen) {
  178.             DoExit();
  179.             }
  180.         }
  181.         }
  182.         exit(2);
  183.     } else {
  184.         /*
  185.          * If we can't write because it'd block, we must be transfering
  186.          * from local to remote. In such a case, we ignore further input
  187.          * from stdin, and wait for the output socket to become writable.
  188.          * Drain() will reset the handler for 0.
  189.          */
  190.         Rpc_Ignore(0);
  191.         Rpc_Watch(rmt, RPC_READABLE|RPC_WRITABLE, Drain, (Rpc_Opaque)0);
  192.     }
  193.     }
  194. }
  195. void
  196. SwapExit(length, data)
  197.     int              length;
  198.     Exit_Data      *data;
  199. {
  200.     Rpc_SwapLong(sizeof(long), &data->id);
  201.     Rpc_SwapLong(sizeof(long), &data->status);
  202. }
  203.  
  204.  
  205. /*-
  206.  *-----------------------------------------------------------------------
  207.  * Exit --
  208.  *    Handle CUSTOMS_EXIT call from import server. This process doesn't
  209.  *    actually exit until we get an end-of-file on the socket to the
  210.  *    remote side. This allows any error message from the customs agent
  211.  *    to be printed before we exit.
  212.  *
  213.  * Results:
  214.  *    None.
  215.  *
  216.  * Side Effects:
  217.  *    exitSeen is set true and status is set to the returned status.
  218.  *
  219.  *-----------------------------------------------------------------------
  220.  */
  221. void
  222. Exit(from, msg, len, data)
  223.     struct sockaddr_in    *from;
  224.     Rpc_Message          msg;
  225.     int                  len;
  226.     Rpc_Opaque           data;
  227. {
  228.     int                  nb;
  229.     Exit_Data          *eVal = (Exit_Data *)data;
  230.     
  231.     status.w_status = eVal->status;
  232.     exitSeen = 1;
  233.     Rpc_Return(msg, 0, (Rpc_Opaque)0);
  234.  
  235.     while ((ioctl(rmt, FIONREAD, &nb) == 0) && (nb > 0)) {
  236. #ifdef notdef
  237.     printf("Exit: %d bytes remaining\n", nb);
  238.     fflush(stdout);
  239. #endif /* notdef */
  240.     Transfer(rmt, 1);
  241.     }
  242.     DoExit();
  243. }
  244.  
  245.  
  246. /*-
  247.  *-----------------------------------------------------------------------
  248.  * DoExit --
  249.  *    Exit in the same way the remote process did.
  250.  *
  251.  * Results:
  252.  *    None.
  253.  *
  254.  * Side Effects:
  255.  *    The process will change state, either stopping, dying horribly
  256.  *    or just exiting cleanly.
  257.  *
  258.  *-----------------------------------------------------------------------
  259.  */
  260. void
  261. DoExit()
  262. {
  263.     if (WIFSTOPPED(status)) {
  264.     int oldmask;
  265.     
  266.     signal (status.w_stopsig, SIG_DFL);
  267.     oldmask = sigsetmask(0);
  268.     kill (getpid(), status.w_stopsig);
  269.     (void)sigsetmask(oldmask);
  270.     signal (status.w_stopsig, PassSig);
  271.     } else if (WIFSIGNALED(status)) {
  272.     if (status.w_coredump) {
  273.         /*
  274.          * We don't want our core dump messing up the other one,
  275.          * so we change to a (we hope) non-writable directory before
  276.          * commiting suicide.
  277.          */
  278.         chdir ("/");
  279.     }
  280.     signal (status.w_termsig, SIG_DFL);
  281.     kill (getpid(), status.w_termsig);
  282.     } else {
  283.     exit (status.w_retcode);
  284.     }
  285. }
  286.  
  287.     
  288. /*-
  289.  *-----------------------------------------------------------------------
  290.  * main --
  291.  *    Usage:
  292.  *        export -id connection-fd return-fd id
  293.  *        export <command>
  294.  *
  295.  *    In the first form, the idea is the exporting program will have
  296.  *    contacted the customs agent already and told it what to do. This
  297.  *    program then simply shuffles I/O and passes signals and the exit
  298.  *    status along...
  299.  *
  300.  *    In the second form, this will
  301.  *
  302.  * Results:
  303.  *    The exit status of the remote process.
  304.  *
  305.  * Side Effects:
  306.  *    Well...
  307.  *
  308.  *-----------------------------------------------------------------------
  309.  */
  310. main (argc, argv)
  311.     int        argc;     /* Number of arguments */
  312.     char    **argv;    /* The arguments themselves */
  313. {
  314.     ExportPermit      permit;
  315.     int                  raLen;
  316.     struct servent      *sep;
  317.  
  318.     if (argc < 2) {
  319.     printf ("Usage:\n\t%s -id <connection-fd> <return-fd> <id>\nor",
  320.         argv[0]);
  321.     printf ("\t%s <command>\n", argv[0]);
  322.     exit(1);
  323.     }
  324.  
  325.     if (strcmp (argv[1], "-id") == 0) {
  326.     if (argc < 5) {
  327.         printf ("Usage:\n\t%s -id <connection-fd> <return-fd> <id>\n",
  328.             argv[0]);
  329.         exit(1);
  330.     }
  331.     rmt = atoi (argv[2]);
  332.     ret = atoi(argv[3]);
  333.     (void)sscanf(argv[4], "%x", &id);
  334.     fflush(stdout);
  335.     permit.id = 0;
  336.     } else {
  337.     rmt = Customs_RawExport(argv[1], &argv[1], (char *)NULL, 0,
  338.                 &customs_Socket, &permit);
  339.     if (rmt < 0) {
  340.         printf ("%s: could not export command\n", argv[0]);
  341.         fflush(stdout);
  342.         execvp(argv[1], &argv[1]);
  343.         printf ("%s: not found\n", argv[1]);
  344.         fflush(stdout);
  345.         exit(3);
  346.     } else {
  347.         ret = customs_Socket;
  348.         id = permit.id;
  349.     }
  350.     }
  351.  
  352.     /*
  353.      * Install RPC server for the remote server to return the exit status
  354.      * of the process.
  355.      */
  356.     Rpc_ServerCreate(ret, (Rpc_Proc)CUSTOMS_EXIT, Exit,
  357.              SwapExit, Rpc_SwapNull, (Rpc_Opaque)0);
  358.     
  359.     if (permit.id != 0) {
  360.     /*
  361.      * Only do this if we did the exportation ourselves
  362.      */
  363.     raLen = sizeof(server);
  364.     if (getpeername (rmt, &server, &raLen) < 0) {
  365.         perror ("getpeername");
  366.         exit(2);
  367.     } else {
  368.         struct hostent *he;
  369.         
  370.         he = gethostbyaddr (&server.sin_addr, sizeof(server.sin_addr),
  371.                 AF_INET);
  372.         if (he == (struct hostent *)NULL) {
  373.         printf ("Connected to unknown host?\n");
  374.         } else {
  375.         printf ("*** exported to %s\n", he->h_name);
  376.         fflush (stdout);
  377.         strcpy (argv[1], he->h_name);
  378.         }
  379.     }
  380.     }
  381.  
  382.     sep = getservbyname("customs", "udp");
  383.     if (sep == NULL) {
  384.     printf("customs/udp unknown\n");
  385.     exit(1);
  386.     }
  387.     server.sin_port = htons(sep->s_port);
  388.  
  389.     signal (SIGHUP, PassSig);
  390.     signal (SIGINT, PassSig);
  391.     signal (SIGQUIT, PassSig);
  392.     signal (SIGTERM, PassSig);
  393.     signal (SIGTSTP, PassSig);
  394.     signal (SIGCONT, PassSig);
  395.     signal (SIGTTOU, PassSig);
  396.     signal (SIGTTIN, PassSig);
  397.     signal (SIGWINCH, PassSig);
  398.  
  399.     signal (SIGPIPE, SIG_IGN);
  400.  
  401.     /*
  402.      * We want to avoid I/O deadlock, so place the remote socket into
  403.      * non-blocking mode, allowing us to delay transfering our own input until
  404.      * the remote side can handle it while still accepting its output.
  405.      */
  406.     fcntl(rmt, F_SETFL, FNDELAY);
  407.  
  408.     /*
  409.      * Install Drain as the initial stream server for rmt to make
  410.      * sure it's writable before bothering to read anything from 0...
  411.      */
  412.     Rpc_Watch(rmt, RPC_READABLE|RPC_WRITABLE, Drain, (Rpc_Opaque)0);
  413.     Rpc_Run();
  414. }
  415.